using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.IO;

namespace fdel
{
    class Program
    {
        static bool verboseFlag = false;
        static bool simulateFlag = false;
        static bool daysFlag = false;
        static bool countFlag = false;
        static bool regexFlag = false;
        static int num = 0;
        static string regex = "";
        static string path;

        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                help();
            }
            else
            {
                getArgs(args);
                processDir();
            }
        }

        static void getArgs(string[] args)
        {
            for (int i = 0; i < args.Length - 1; i++)
            {
                switch (args[i].ToUpper())
                {
                    case "/V":
                        verboseFlag = true;
                        log("Verbose flag ON");
                        break;
                    case "/S":
                        verboseFlag = true;
                        simulateFlag = true;
                        log("Simulate flag ON");
                        break;
                    case "/D":
                        daysFlag = true;
                        num = getInt(args[++i]);
                        log(string.Format("Deleting all files older than {0} days", num));
                        break;
                    case "/C":
                        countFlag = true;
                        num = getInt(args[++i]);
                        log(string.Format("Deleting all files older than the newest {0} files", num));
                        break;
                    case "/R":
                        regexFlag = true;
                        regex = args[++i];
                        log(string.Format("Deleting all files that match [{0}]", regex));
                        break;
                    case "/H":
                        help();
                        break;
                    default:
                        log(Console.Error, string.Format("Unknown Argument [{0}]", args[i]));
                        break;
                }
            }

            path = args[args.Length - 1];
        }

        static void processDir()
        {
            DirectoryInfo di = new DirectoryInfo(path);
            List<FileSystemInfo> children = new List<FileSystemInfo>();
            Regex re = null;

            if (regexFlag)
            {
                re = new Regex(regex);
            }

            if (di.Exists)
            {
                log(string.Format("Found {0} items in [{1}]", di.GetFileSystemInfos().Length, di.Name));
                log(string.Format("{0} Files and {1} Directories", di.GetFiles().Length, di.GetDirectories().Length));
                foreach (FileSystemInfo child in di.GetFileSystemInfos())
                {
                    if (regexFlag)
                    {
                        if (!re.Match(child.Name).Success)
                        {
                            log(string.Format("[{0}] did NOT match", child.Name));
                            continue;
                        }
                    }
                    children.Add(child);
                }

                if (daysFlag || countFlag)
                {
                    children.Sort(delegate(FileSystemInfo x, FileSystemInfo y) 
                    {
                        return x.LastWriteTime.CompareTo(y.LastWriteTime);
                    });
                    children.Reverse();
                }

                int count = 0;
                foreach (FileSystemInfo fsi in children)
                {
                    if (daysFlag)
                    {
                        if (fsi.LastWriteTime < DateTime.Now.Subtract(TimeSpan.FromDays(num)))
                        {
                            log(string.Format("marking [{1}] for deletion - {0}", fsi.LastWriteTime, fsi.Name));
                            deleteItem(fsi);
                        }
                        else
                        {
                            log(string.Format("[{0}] will NOT be marked for deletion - {1}", fsi.Name, fsi.LastWriteTime));
                        }
                    }
                    else if (countFlag)
                    {
                        if (++count > num)
                        {
                            log(string.Format("marking [{2}] for deletion (item {0}) - {1}", count, fsi.LastWriteTime, fsi.Name));
                            deleteItem(fsi);
                        }
                        else
                        {
                            log(string.Format("[{0}] will NOT be marked for deletion - {1}", fsi.Name, fsi.LastWriteTime));
                        }
                    }
                    else if (regexFlag)
                    {
                        log(string.Format("marking [{0}] for deletion", fsi.Name));
                        deleteItem(fsi);
                    }
                }
            }
            else
            {
                log(Console.Error, string.Format("[{0}] does not exist or is not a directory"));
            }
        }

        static void deleteItem(FileSystemInfo fsi)
        {
            if (simulateFlag)
            {
                log(string.Format("[SIMULATE] Deleting {0}", fsi.FullName));
            }
            else
            {
                try
                {
                    log(string.Format("Deleting {0}...", fsi.FullName));
                    if (fsi is FileInfo)
                    {
                        fsi.Delete();
                    }
                    else if (fsi is DirectoryInfo)
                    {
                        (fsi as DirectoryInfo).Delete(true);
                    }
                    log("SUCCESS");
                }
                catch (Exception e)
                {
                    log(Console.Error, "FAIL");
                    log(Console.Error, e.ToString());
                }
            }
        }

        static int getInt(string arg)
        {
            int num = 0;
            try
            {
                num = int.Parse(arg);
            }
            catch (Exception e)
            {
                log(Console.Error, e.ToString());
            }
            return num;
        }

        static void log(string text)
        {
            log(Console.Out, text);
        }

        static void log(TextWriter w, string text)
        {
            if (verboseFlag)
            {
                w.WriteLine(text);
            }
        }

        static void help()
        {
            Console.WriteLine(
@"fdel.exe
Filtered Delete
Delete files based on a filter

fdel.exe [options] path
options:
    /v          : verbose mode (print info to stdout/stderr
    /s          : simulate mode (don't actually delete anything) [implies verbosity]
    /d <days>   : delete all files older than <days> days
    /c <count>  : delete all but the newest <count> files
    /r <regex>  : delete all files matching <regex>
    /h          : show this help
"
            );
        }
    }
}
